package tech.yixiyun.framework.kuafu.controller.request;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import tech.yixiyun.framework.kuafu.config.AppConfig;
import tech.yixiyun.framework.kuafu.config.ConfigKey;
import tech.yixiyun.framework.kuafu.context.ApplicationContext;
import tech.yixiyun.framework.kuafu.controller.action.ActionContext;
import tech.yixiyun.framework.kuafu.shutdown.ShutdownRegistry;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.FileCleanerCleanup;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileCleaningTracker;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class UploadHelper {


    private static boolean hasRegisted = false;

    /**
     * 注册上传的临时文件销毁器，commons-fileupload提供的
     */
    public static void registerUploadCleaner() {
        if (hasRegisted) return;
        ApplicationContext.getServletContext().setAttribute(FileCleanerCleanup.FILE_CLEANING_TRACKER_ATTRIBUTE, new FileCleaningTracker());
        hasRegisted =true;
        ShutdownRegistry.register(() -> {
            closeUploadCleaner();
        });
    }

    /**
     * 关闭UploadCleaner
     */
    public static void closeUploadCleaner() {
        if (hasRegisted) {
            ((FileCleaningTracker)ApplicationContext.getServletContext().getAttribute(FileCleanerCleanup.FILE_CLEANING_TRACKER_ATTRIBUTE)).exitWhenFinished();
        }
    }


    /**
     * 禁止上传的文件类型
     */
    private static Set<String> notAllowSuffix = new HashSet<>();

    static {
        String[] suffixs = AppConfig.getAsObject(ConfigKey.UPLOAD_NOTALLOWSUFFIX, String[].class);
        if (suffixs != null && suffixs.length>0) {
            for (String suffix : suffixs) {
                notAllowSuffix.add(suffix.toLowerCase());
            }
        }

    }


    /**
     * 上传相关
     */
    private static final DiskFileItemFactory factory = new DiskFileItemFactory();

    /**
     * multipart形式提交的表单数据，如果是文件，value存的就是临时文件地址。
     * 临时文件将以 randomstr_uuid_randomstr_originalname 的格式命名。
     *
     * @return
     */
    public static void resolveMultipartParameters(HttpServletRequest requeset, Map<String, String[]> map) {
        ServletFileUpload upload = new ServletFileUpload(factory);
        long singleMaxSize = ActionContext.getAction().getRoute().getSingleMaxSize();
        upload.setFileSizeMax(singleMaxSize * 1000); //单位字节
        long totalMaxSize = ActionContext.getAction().getRoute().getTotalMaxSize();
        upload.setSizeMax(totalMaxSize * 1000); //单位字节
        try {
            List<FileItem> items = upload.parseRequest(requeset);
            String value;
            for (FileItem item : items) {
                if (item.isFormField()) { //普通form表单字段
                    value = item.getString("utf-8");
                } else { //上传的文件
                    if (checkIsAllowUpload(item.getName()) == false) {
                        throw new RuntimeException("不允许上传的文件类型");
                    }
                    value = getTempPath((DiskFileItem)item);
                }
                String[] currentValue = map.get(item.getFieldName());
                if (currentValue == null) {
                    map.put(item.getFieldName(), new String[]{value});
                } else {
                    map.put(item.getFieldName(), ArrayUtil.append(currentValue, value));
                }
            }
        } catch (FileUploadBase.FileSizeLimitExceededException e) {
            throw new RuntimeException("上传的单个文件不能超过"+singleMaxSize+"K");
        } catch (FileUploadBase.SizeLimitExceededException e) {
            throw new RuntimeException("单次请求内容超出"+totalMaxSize+"K大小限制");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 检测是否允许上传
     * @param name
     * @return
     */
    private static boolean checkIsAllowUpload(String name) {
        int i = name.lastIndexOf(".");
        if (i== -1) {
            return true;
        } else {
            return notAllowSuffix.contains(name.substring(i).toLowerCase()) == false;
        }
    }


    /**
     * 获取文件的临时保存路径
     * @param item
     * @return
     */
    private static String getTempPath(DiskFileItem item) {
        if (item.getSize() == 0) return null;
        String randomPrefix = RandomUtil.randomString(4);
        File repository = (File) ApplicationContext.getServletContext().getAttribute("javax.servlet.context.tempdir");

        String path = repository.toURI().getPath() + randomPrefix + "_" + UUID.fastUUID().toString(true) + "_" + randomPrefix + "_" + item.getName();
        File newFile = new File(path);
        if (item.isInMemory()) {
            try {
                item.write(newFile);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            File now = item.getStoreLocation();
            if (now == null) {
                throw new RuntimeException("无法保存上传文件到硬盘");
            }

            now.renameTo(newFile);
        }
        return path;
    }

}
